﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Sockets;
using System.Text;
using System.Threading.Tasks;
using NLog;
using NModbus;

namespace ModuleBase
{
    public class PLCService : IPLCService
    {
        private static Logger logger = LogManager.GetCurrentClassLogger();
        IModbusMaster master;
        TcpClient client;
        public bool Connected { get; set; }
        public void Close()
        {
            try
            {
                Connected = false;
                master?.Dispose();
                client?.Close();
                client?.Dispose();
            }
            catch { }
        }

        public bool Connect(string ip)
        {
            try
            {
                client = new TcpClient(ip, 502);
                var factory = new ModbusFactory();
                master = factory.CreateMaster(client);
                Connected = true;
                return true;
            }
            catch (Exception ex)
            {
                logger.Error(ex);
                Connected = false;
                return false;
            }
        }

        public bool[] ReadXCoils(int addr, int count)
        {
            return master.ReadCoils(1, (ushort)(0xF800 + addr), (ushort)count);
        }
        public bool[] ReadYCoils(int addr, int count)
        {
            return master.ReadCoils(1, (ushort)(0xFC00 + addr), (ushort)count);
        }
        public void WriteYCoil(int addr, bool val)
        {
            master.WriteSingleCoil(1, (ushort)(0xFC00 + addr), val);
        }
        public ushort[] ReadDRegisters(int addr, int count)
        {
            return master.ReadHoldingRegisters(1, (ushort)addr, (ushort)count);
        }
        public void WriteSingleRegister(int addr, int value)
        {
            var a1 = Convert.ToUInt16(((short)value).ToString("X4"), 16);
            master.WriteSingleRegister(1, (ushort)addr, a1);
        }
        public void WriteMultipleRegisters(int addr, int[] data)
        {
            ushort[] a1 = new ushort[data.Length];
            for (int i = 0; i < data.Length; i++)
            {
                a1[i] = Convert.ToUInt16(((short)data[i]).ToString("X4"), 16);
            }

            master.WriteMultipleRegisters(1, (ushort)addr, a1);
        }
        public void WriteRegisters(int addr, int[] value)
        {
            ushort[] _values = new ushort[value.Length];
            for (int i = 0; i < value.Length; i++)
            {
                _values[i] = Convert.ToUInt16(((short)value[i]).ToString("X4"), 16);
                master.WriteMultipleRegisters(1, (ushort)addr, _values);
            }
        }
        public bool[] ReadMCoils(int addr, int count)
        {
            return master.ReadCoils(1, (ushort)addr, (ushort)count);
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="addr"></param>
        /// <param name="val"></param>
        /// <param name="maxCount">0:只写1次 >0:写指定次数 <0:写无数次"</param>
        public void WriteMCoil(int addr, bool val, int maxCount = 0)
        {
            bool isSuccess = false;
            do
            {
                try
                {
                    master.WriteSingleCoil(1, (ushort)addr, val);
                    isSuccess = true;
                }
                catch (Exception ex)
                {
                    logger.Error(ex);
                }
                if (maxCount > 0)
                {
                    maxCount--;
                }
            } while (maxCount != 0 && !isSuccess);

        }
        public void WriteMCoils(int addr, bool[] val)
        {
            master.WriteMultipleCoils(1, (ushort)addr, val);
        }
        public float ReadFloat(int addr)
        {
            var regs = master.ReadHoldingRegisters(1, (ushort)addr, 2);
            var hexstr = string.Join("", from p in regs select p.ToString("X4"));
            var hexstr1 = hexstr.Substring(4, 4) + hexstr.Substring(0, 4);//高低位取反
            byte[] raw = new byte[hexstr1.Length / 2];
            for (int i = 0; i < raw.Length; i++)
            {
                // THEN DEPENDING ON ENDIANNESS
                //raw[i] = Convert.ToByte(hexstr1.Substring(i * 2, 2), 16);
                // OR
                raw[raw.Length - i - 1] = Convert.ToByte(hexstr1.Substring(i * 2, 2), 16);
            }
            return BitConverter.ToSingle(raw, 0);
        }
        public void WriteFloat(int addr, float value)
        {
            byte[] vOut = BitConverter.GetBytes(value);
            ushort[] ushorts = new ushort[vOut.Length / 2];
            for (int i = 0; i < vOut.Length / 2; i++)
            {
                string str = vOut[i * 2 + 1].ToString("X2") + vOut[i * 2].ToString("X2");
                ushorts[i] = Convert.ToUInt16(str, 16);
            }
            master.WriteMultipleRegisters(1, (ushort)addr, ushorts);
        }
        public void WriteFloats(int addr, float[] value)
        {
            ushort[] ushorts = new ushort[2 * value.Length];
            for (int i = 0; i < value.Length; i++)
            {
                byte[] vOut = BitConverter.GetBytes(value[i]);

                for (int j = 0; j < vOut.Length / 2; j++)
                {
                    string str = vOut[j * 2 + 1].ToString("X2") + vOut[j * 2].ToString("X2");
                    ushorts[j + i * 2] = Convert.ToUInt16(str, 16);
                }
            }
            master.WriteMultipleRegisters(1, (ushort)addr, ushorts);
        }
    }
}
